#include "iSocket.h"
#if defined(__WIN__)
#include <string>
typedef WSABUF iovec;
#elif defined(__LINUX__)
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#define INVALID_SOCKET -1
#endif
#include <stdlib.h>
#include "../util/src/iIOR.h"
#include "../util/src/iIOThread.h"
#include "../util/h/iMsgDB.h"
#include "../util/h/iUtil.h"
#include "../util/h/iLogger.h"

#define DEBUG(x) iDEBUG(MOD_TP,x)
#define FUNC(x)  iFUNC(MOD_TP,x)
#define INFO(x)  iINFO(MOD_TP,x)

soctx* soctx::new_instance(const char* address, iAllocator* alloc)
{
    if(NULL == address)
        return new(alloc->allocate(sizeof(soctx))) soctx(alloc);

    char proto[32]={0};
    char ip[64] = {0};
    char port[8] = {0};
    if(!parse_address(address, proto, ip, port))
        return NULL;
   
    if(strstr(proto, "UDP"))
        return new(alloc->allocate(sizeof(soctx))) soctx(soctx::UDP, ip, port, alloc);
    else
        return new(alloc->allocate(sizeof(soctx))) soctx(soctx::TCP, ip, port, alloc);
}
soctx::soctx(iAllocator* alloc)
:iAllocator::Object(alloc)
,m_family(AF_UNSPEC)
{
}
soctx::soctx(PROTOCOL protocol, const char* ip, const char* port, iAllocator* alloc)
:iAllocator::Object(alloc)
,m_family(AF_UNSPEC)
,m_protocol(protocol)
{
	char service[8] = {0};
    strcpy(service, port);
	struct addrinfo *result = NULL;
	struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_protocol = protocol==UDP ? IPPROTO_UDP :IPPROTO_TCP;
	hints.ai_socktype = protocol==UDP ? SOCK_DGRAM : SOCK_STREAM;
	hints.ai_flags = AI_NUMERICHOST;
    if(port==0 || *port=='0')
        hints.ai_flags |= AI_PASSIVE;
    else
        hints.ai_flags |= AI_NUMERICSERV;
	int errCode = getaddrinfo(ip, service, &hints, &result);
	if(0 == errCode)
	{
		m_family = result->ai_family;
		if(m_family & AF_INET)
			m_addr.sa4 = *(struct sockaddr_in*)result->ai_addr;
		else
			m_addr.sa6 = *(struct sockaddr_in6*)result->ai_addr;
		freeaddrinfo(result);
	}
	else
	{
		printf("err:%s\n", gai_strerror(errCode));
	}
}
soctx::~soctx()
{
}
soctx& soctx::operator=(const soctx& ctx)
{
	m_family = ctx.m_family;
	m_protocol = ctx.m_protocol;
	m_addr = ctx.m_addr;
	return *this;
}
void soctx::release()
{
    m_allocator->deallocate(this);
}
//////////////////////////////////////////////////////////////////////////
/// iSocket
//////////////////////////////////////////////////////////////////////////
iSocket::iSocket(soctx* local, soctx* remote)
:iIO(INVALID_SOCKET)
{
    m_local = NULL;
    m_remote = NULL;

    m_local = soctx::new_instance(NULL);
    *m_local = *local;
    if(remote)
    {
        m_remote = soctx::new_instance(NULL);
        *m_remote = *remote;
    }
}
iSocket::~iSocket()
{
    m_local->release();
    m_local = NULL;
}
bool iSocket::bind(soctx& so)
{
	os_socket_t sock = INVALID_SOCKET;
	if(so.get_family() & AF_INET)
	{
		sock = socket(AF_INET, so.get_protocol()==soctx::UDP ? SOCK_DGRAM:SOCK_STREAM, so.get_protocol()==soctx::UDP ? IPPROTO_UDP:IPPROTO_TCP);
        if(::bind(sock, (struct sockaddr*)&so.get_sa4(), sizeof(so.get_sa4())))
            return false;

	}
	else if(so.get_family() & AF_INET6)
	{
		sock = socket(AF_INET6, so.get_protocol()==soctx::UDP ? SOCK_DGRAM:SOCK_STREAM, so.get_protocol()==soctx::UDP ? IPPROTO_UDP:IPPROTO_TCP);
	}
	if(sock == INVALID_SOCKET)
		return false;

	m_io = sock;
	return true;
}
int32_t iSocket::send(iMsgDB* db)
{
    iovec iv[32] = {0};
    int cnt = 0;
    iMsgDB* v = db;
    while(v)
    {
#if defined(__WIN__)
		iv[cnt].buf = (char*)v->get_read_cursor();
		iv[cnt].len = v->get_data_length();
#elif defined(__LINUX__)
        iv[cnt].iov_base = v->get_read_cursor();
        iv[cnt].iov_len = v->get_data_length();
#endif
        cnt++;
        v = v->next();
    }

	int32_t ret = 0;
    unsigned long size = 0;
    if(is_udp())
    {
#if defined(__WIN__)
		ret = WSASendTo(m_io, iv, cnt, &size, 0, (sockaddr *)&m_remote->get_sa4(), m_remote->get_family()==AF_INET ? sizeof(m_remote->get_sa4()):sizeof(m_remote->get_sa6()), NULL, NULL);
#elif defined(__LINUX__)
        msghdr msg; 
        memset(&msg, 0, sizeof(msg));   
        msg.msg_iov = iv;
        msg.msg_iovlen = cnt; 
        msg.msg_name = &m_remote->get_sa4();
        msg.msg_namelen = m_remote->get_family()==AF_INET ? sizeof(m_remote->get_sa4()):sizeof(m_remote->get_sa6());
        size = ::sendmsg(m_io, &msg, 0);
#endif
    }
    else
    {
#if defined(__WIN__)
		ret = WSASend(m_io, iv, cnt, &size, 0, NULL, 0);
#elif defined(__LINUX__)
        size = ::writev(m_io, iv, cnt);
		if(-1 == size)
		{
			size = 0;
			ret = errno;
		}
#endif
    }

    
    if( ret )
    {
        INFO("writev failed, err="<<errno);
    }
    else if(size != cnt)
    {
        m_mask |= IO_MASK_WRITE;
        DEBUG("writev size="<<size);
    }

    return size;
}
void iSocket::close()
{
    #if defined(__WIN__)
    closesocket(m_io);
    #elif defined(__LINUX_)
    ::close(m_io);
    #endif
}
//////////////////////////////////////////////////////////////////////////
///	iClientSocket
//////////////////////////////////////////////////////////////////////////
iClientSocket::iClientSocket(soctx* local, soctx* remote)
:iSocket(local, remote)
{
	m_mask = IO_MASK_CONNECT|IO_MASK_READ;
}
iClientSocket::~iClientSocket()
{
    m_remote->release();
    m_remote = NULL;
}
void iClientSocket::connect()
{
    m_mask |= IO_MASK_WRITE;
        
    #if defined(__WIN__)
    unsigned long flags = 1;
    ioctlsocket(m_io, FIONBIO, &flags);
    int32_t len = m_local->get_family()==AF_INET ? sizeof(m_remote->get_sa4()):sizeof(m_remote->get_sa6());
    #elif defined(__LINUX__)
    int32_t flags = ::fcntl(m_io, F_GETFL, 0);
    flags |= O_NONBLOCK;
    fcntl(m_io, F_SETFL, flags);
    socklen_t len = m_local->get_family()==AF_INET ? sizeof(m_remote->get_sa4()):sizeof(m_remote->get_sa6());
    #endif

    ::connect(m_io, m_local->get_family()==AF_INET ? (struct sockaddr*)&m_remote->get_sa4():(struct sockaddr*)&m_remote->get_sa6(), len);
    if(m_remote->get_protocol()==soctx::UDP)
        on_write();
}
void iClientSocket::attach(os_io_t io)
{
	if(m_io != INVALID_SOCKET)
		return;
	m_io = io;
}
int iClientSocket::on_read() 
{
    const uint32_t block_size=1500;
	iMsgDB::iDataBlock::sp db = iMsgDB::iDataBlock::new_instance(NULL, block_size);
	int32_t read_bytes = ::recv(m_io, (char*)db->get_buffer(), block_size, 0);
	if(read_bytes & 0x80000000) // < 0
	{
        if(ESO_WOULDBLOCK == errno)
            return 0;
        else if (ESO_EAGAIN == errno)
            return 0;
        else
            m_sink->on_close();
	}
	else if(read_bytes == 0)
	{
		m_sink->on_close();
	}
	else
	{
		db->set_length(read_bytes);
        iMsgDB* mdb = iMsgDB::new_instance(NULL, 0);
        mdb->write(db);
		m_sink->on_data_indication(mdb);
        return read_bytes;
	}

	return 0; 
}
int iClientSocket::on_write()
{
    if(m_mask & IO_MASK_CONNECT)
    {
        m_mask &= ~(IO_MASK_WRITE|IO_MASK_CONNECT);
        #if defined(__WIN__)
        int32_t len = m_local->get_family()==AF_INET ? sizeof(m_remote->get_sa4()):sizeof(m_remote->get_sa6());
        #elif defined(__LINUX__)
        socklen_t len = m_local->get_family()==AF_INET ? sizeof(m_remote->get_sa4()):sizeof(m_remote->get_sa6());
        #endif
        ::getsockname(m_io, m_local->get_family()==AF_INET ? (struct sockaddr*)&m_local->get_sa4():(struct sockaddr*)&m_local->get_sa6(), &len);
        m_sink->on_connect_confirm();
        return 0; 
    }
    else
    {
        m_mask &= ~IO_MASK_WRITE;
        m_sink->on_write();
		return 0;
    }
}
int iClientSocket::on_error()
{
	m_sink->on_close();
	return 0; 
}

//////////////////////////////////////////////////////////////////////////
/// iListenSocket
//////////////////////////////////////////////////////////////////////////
iListenSocket::iListenSocket(soctx* local)
:iSocket(local, NULL)
{
	m_mask = IO_MASK_ACCEPT;
}
iListenSocket::~iListenSocket()
{
}
int32_t iListenSocket::listen()
{
    if(m_local->get_protocol()==soctx::UDP)
        return 0;

    int32_t optval=1;
    setsockopt(m_io,SOL_SOCKET,SO_REUSEADDR,(const char *)&optval,sizeof(int32_t));
    int err = ::listen(m_io, 512);
	if(err)
    {
        #if defined(__WIN__)
		return WSAGetLastError();
        #elif defined(__LINUX__)
        return err;
        #endif
    }

    return 0;
}
int iListenSocket::on_read()
{   
    switch(this->m_local->get_protocol())
    {
	case soctx::TCP:
        {
            soctx* ctx = soctx::new_instance(NULL);
            *ctx = *m_local;
            #if defined(__WIN__)
            int len = m_local->get_family()==AF_INET ? sizeof(ctx->get_sa4()) : sizeof(ctx->get_sa6());
            #elif defined(__LINUX__)
            socklen_t len = m_local->get_family()==AF_INET ? sizeof(ctx->get_sa4()) : sizeof(ctx->get_sa6());
            #endif
            os_socket_t so = accept(m_io, m_local->get_family()==AF_INET ? (struct sockaddr*)&ctx->get_sa4():(struct sockaddr*)&ctx->get_sa6(), &len);
            iClientSocket* cs = new iClientSocket(m_local, ctx);
            cs->attach(so);
            m_sink->on_connect_indication(cs);
            ctx->release();
        }
        break;
    case soctx::UDP:
        {
            class UDPFilter
            {
                public:
                    inline UDPFilter(soctx* local, os_io_t& io) : m_io(io)
                    {
                        m_local = soctx::new_instance(NULL);
                        *m_local = *local;
                        m_port_indicator.resize(0xFFFF);
                    }
                    inline void dalegate(sockaddr_in& sa4, iSocketSink* sink, iMsgDB* mdb)
                    {
                        iClientSocket* cs = NULL;
                        if(!m_port_indicator[sa4.sin_port].empty())
                        {
                            for(std::list<iClientSocket*>::iterator iter=m_port_indicator[sa4.sin_port].begin(); iter!=m_port_indicator[sa4.sin_port].end();++iter)
                            {
                                if((*iter)->get_remote()->get_sa4().sin_addr.s_addr==sa4.sin_addr.s_addr)
                                {
                                    cs = *iter;
                                    break;
                                }
                            }
                        }

                        if(cs == NULL)
                        {
                            soctx* ctx = soctx::new_instance(NULL);
                            *ctx = *m_local;
                            ctx->get_sa4() = sa4;
                            cs = new iClientSocket(m_local, ctx);
                            cs->attach(m_io);
                            sink->on_connect_indication(cs);
                            m_port_indicator[sa4.sin_port].push_back(cs);
                        }
                        cs->get_sink()->on_data_indication(mdb);
                    }
                private:
                    os_io_t& m_io;
                    soctx* m_local;
                    std::vector< std::list<iClientSocket*> > m_port_indicator;
            };
            static UDPFilter uft(m_local, m_io);
            const uint32_t block_size=1500;
            iMsgDB::iDataBlock::sp db = iMsgDB::iDataBlock::new_instance(NULL, block_size);
            sockaddr_in sa4;
            #if defined(__WIN__)
			int len = 0;
            #elif defined(__LINUX__)
            socklen_t len = sizeof(sa4);
            #endif
            int32_t read_bytes = ::recvfrom(m_io, (char*)db->get_buffer(), block_size, 0, (struct sockaddr*)&sa4, &len);
            if(read_bytes)
            {
                iMsgDB* mdb = iMsgDB::new_instance(NULL,0);
                db->set_length(read_bytes);
                mdb->write(db);
                uft.dalegate(sa4, m_sink, mdb);
            }
        }
        break;
    }

	return 0;
}
int iListenSocket::on_write()
{
	printf("on write call\n");

	return 0;
}
int iListenSocket::on_error()
{
	printf("on error call\n");

	return 0;
}
